package com.ouroboroswiki.core.content;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import com.ouroboroswiki.core.AbstractContentRepository;
import com.ouroboroswiki.core.Content;
import com.ouroboroswiki.core.ContentException;
import com.ouroboroswiki.core.ContentPath;
import com.ouroboroswiki.core.ContentRepository;
import com.ouroboroswiki.core.ContentRepositoryFactory;
import com.ouroboroswiki.core.ContentUtil;
import com.ouroboroswiki.core.MultipartVersion;
import com.ouroboroswiki.core.Version;
import com.ouroboroswiki.core.VersionBuilder;

public class PathDelegatingContentRepository extends AbstractContentRepository {

	@SuppressWarnings("unused")
	private static final Logger log = Logger.getLogger( PathDelegatingContentRepository.class.getName() );
	
	private ContentRepositoryFactory repositoryFactory;
	private ContentRepository defaultRepository;
	private boolean trimDefault;
	
	public PathDelegatingContentRepository() {
		
	}
	
	public ContentRepositoryFactory getRepositoryFactory() {
		return repositoryFactory;
	}

	public void setRepositoryFactory(ContentRepositoryFactory repositoryFactory) {
		this.repositoryFactory = repositoryFactory;
	}

	public ContentRepository getDefaultRepository() {
		return defaultRepository;
	}

	public void setDefaultRepository(ContentRepository defaultRepository) {
		this.defaultRepository = defaultRepository;
	}

	public boolean isTrimDefault() {
		return trimDefault;
	}

	public void setTrimDefault(boolean trimDefault) {
		this.trimDefault = trimDefault;
	}

	@Override
	public boolean exists(Object principal, ContentPath path) throws ContentException {
		String key = path.getName();
		boolean[] trimOut = new boolean[1];
		ContentRepository repository = getRepository(key, trimOut);
		
		boolean exists = false;
		if( repository != null ) {
			
			if( trimOut[0] || trimDefault ) {
				List<ContentPath> childPaths = path.getChildren();
				for( int i=0; i<childPaths.size() && !exists; i++ ) {
					ContentPath subpath = childPaths.get( i );
					exists = repository.exists(principal, subpath);
				}
			} else {
				exists = repository.exists(principal, path);
			}
		} else {
			exists = false;
		}
		return exists;
	}

	@Override
	public Content getContent(Object principal, ContentPath path, Version version, Map<String, Object> properties)
			throws ContentException {
		if( path.getChildren() == null || path.getChildren().size() == 0 ) {
			if(  trimDefault ) {
				throw new ContentException( "tried to trim empty path" );
			} else {
				return defaultRepository.getContent(principal, path, version, properties);
			}
		} else {
			String key = path.getName();
			boolean[] trimOut = new boolean[1];
			ContentRepository repository = getRepository(key, trimOut);
			if( repository != null ) {
				if( trimOut[0] || trimDefault ) {
					List<ContentPath> children = path.getChildren();
					HashMap<String, Content> childContents = new HashMap<String, Content>(children.size());
					boolean exists = false;
					boolean writable = children.size() == 1;
					boolean dynamic = false;
					String mimeType = null;
					for( int i=0; i<children.size(); i++ ) {
						ContentPath subpath = children.get( i );
						Content childContent = repository.getContent(principal, subpath, version, properties);
						dynamic = dynamic || childContent.isDynamic();
						exists = exists || childContent.exists();
						writable = writable && childContent.isWritable();
						mimeType = childContent.getMimeType();
						String name;
						if( i == 0 ) {
							name = Content.CHILD_CONTENT_MAIN;
						} else {
							name = Content.CHILD_CONTENT_MAIN + '_' + i;
						}
						childContents.put(name, childContent);
					}
					CompositeContent result = new CompositeContent(childContents);
					result.setExists( exists );
					result.setDynamic( dynamic );
					result.setWritable( writable );
					result.setMimeType( mimeType );
					result.setRepositoryName( this.getName() );
					result.setUniqueName(ContentUtil.toString(path));
					return result;
				} else {
					// TODO this should probably be a composite content anyway
					return repository.getContent(principal, path, version, properties);
				}
			} else {
				throw new ContentException( "no such delegate "+key );
			}
		}
	}
	
	@Override
	public Version buildVersion(Object principal, ContentPath path,
			String[] versionPath, VersionBuilder versionBuilder) throws ContentException {
		if( path.getChildren() == null || path.getChildren().size() == 0 ) {
			if(  trimDefault ) {
				throw new ContentException( "tried to trim empty path" );
			} else {
				return defaultRepository.buildVersion(principal, path, versionPath, versionBuilder);
			}
		} else {
			String key = path.getName();
			boolean[] trimOut = new boolean[1];
			ContentRepository repository = getRepository(key, trimOut);
			if( repository != null ) {
				if( repository != defaultRepository || trimDefault ) {
					List<ContentPath> children = path.getChildren();
					HashMap<String, Version> childVersions = new HashMap<String, Version>( children.size() );
					for( int i=0; i<children.size(); i++ ) {
						ContentPath subpath = children.get( i );
						Version childVersion = repository.buildVersion(principal, subpath, versionPath, versionBuilder);
						childVersions.put( Integer.toString( i ), childVersion );
					}
					return new MultipartVersion(childVersions);
				} else {
					return defaultRepository.buildVersion(principal, path, versionPath, versionBuilder);
				}
			} else {
				throw new ContentException( "no such delegate "+key );
			}
		}
	}

	public ContentRepository getRepository( String key, boolean[] trimOut ) {
		ContentRepository repository = this.repositoryFactory.getRepository( key );
		if( repository == null ) {
			repository = defaultRepository;
			trimOut[0] = false;
		} else {
			trimOut[0] = true;
		}
		return repository;
	}
}
